package com.qf.vote2203.dbvote.service.impl;

import cn.hutool.json.JSONUtil;
import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTHeader;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qf.vote2203.dbvote.entity.Vote;
import com.qf.vote2203.dbvote.mapper.VoteMapper;
import com.qf.vote2203.dbvote.service.IVoteService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qf.vote2203.utils.Constants;
import com.qf.vote2203.utils.LocalCache;
import net.bytebuddy.implementation.bytecode.Throw;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author dong
 * @since 2023-02-27
 */
@Service
public class VoteServiceImpl extends ServiceImpl<VoteMapper, Vote> implements IVoteService {

    @Autowired
    RedisTemplate redisTemplate;

    @Autowired
    RabbitTemplate rabbitTemplate;

    @Override
    public String getToken(String phone) throws Exception {

        //1. 检查一下该请求的 手机号是否 已经有可以使用的token （时间间隔在1分钟之内）
        //key为phone value为token 存在redis的服务器db

        String key = Constants.REDIS_JWT_PRE + phone;

        String redisToken = (String) redisTemplate.opsForValue().get(key);

        if (redisToken == null) {

            //生成一个新的token
            String newToken = createNewToken(key);

            redisTemplate.opsForValue().set(key, newToken, 60, TimeUnit.SECONDS);

            return newToken;

        }else {

            //缓存命中直接返回

            return redisToken;
        }
    }

    /**
     * 投票接口
     * @param phone
     * @param cadidateId
     * @param token
     */
    @Override
    public void doVote(String phone, Integer cadidateId, String token) throws Exception {

        //检查是否满足投票规则(是否超过60秒)
        checkVoteRule(phone,cadidateId,token);

        //投票
        Vote vote = new Vote();
        vote.setPhone(phone);
        vote.setCandidateId(cadidateId);
        vote.setUpdateTime(new Date());
        vote.setVoteNum(1);

        redisTemplate.opsForList().rightPush("votelist",vote);

        // zincrby key increment member

        redisTemplate.opsForZSet().incrementScore("tickettotal",cadidateId,1);

        //写入key
        redisTemplate.opsForValue().set("vote"+phone,"voteFinish",60,TimeUnit.SECONDS);

        //String s = JSONUtil.toJsonStr(vote);
        List<String> votelist = (List<String>) redisTemplate.opsForList().range("votelist",0,-1);
        ArrayList<String> votes = new ArrayList<>();

        ObjectMapper objectMapper = new ObjectMapper();

        for (int i = 0; i < votelist.size(); i++) {

            votes.add(votelist.get(i));

            if(votes.size()==5){

                String json = objectMapper.writeValueAsString(votes);
                rabbitTemplate.convertAndSend("vote_exchange","vote.total",json);

                votes.clear();
            }
        }
        if(votes.size()>0){
            String json = objectMapper.writeValueAsString(votes);
            rabbitTemplate.convertAndSend("vote_exchange","vote.total",json);
        }
    }

    @Override
    public List<Map<String, Object>> statisticVote() {
        Set<ZSetOperations.TypedTuple> set = redisTemplate.opsForZSet().rangeByScoreWithScores("tickettotal", 0, Double.MAX_VALUE);

        ArrayList<Map<String, Object>> data = new ArrayList<>();

        for (ZSetOperations.TypedTuple typedTuple : set) {
            Object value = typedTuple.getValue();
            Double score = typedTuple.getScore();
            HashMap<String, Object> map = new HashMap<>();
            map.put("cadidate_id",value);
            map.put("total",score);
            data.add(map);
        }

        return data;
    }

    private void checkVoteRule(String phone, Integer cadidateId, String token) throws Exception {

        boolean validate = JWT.of(token).setKey(Constants.SECRET.getBytes(StandardCharsets.UTF_8)).validate(0);

        JWT of = JWT.of(token);

        String sub = (String) of.getPayload("sub");

        String[] split = sub.split(":");

        String s = split[split.length-1];

        if(!phone.equals(s)){

            throw new Exception("不是同一个手机号！");
        }

        if(validate){

            Boolean exist = redisTemplate.hasKey("vote"+phone);

            if(exist){

                throw new Exception("每分钟只能投一票！");
            }else {

                return;
            }
        }else {

            throw new Exception("token 失效");
        }
    }

    /**
     * 创建jwttoken
     * @param phone
     * @return
     */
    private String createNewToken(String phone) throws Exception {
        final String jwtToken = JWT.create().setSubject(phone)
                .setExpiresAt(new Date(System.currentTimeMillis() + 1000 * 60))
                .setPayload("createTime",System.currentTimeMillis())
                .setKey(Constants.SECRET.getBytes("utf-8"))
                .sign();

        return jwtToken;
    }
}
